PreVibeRange · system map · v0 draft · 2026.04.20
Three primitives that stack. pretext measures text without touching the DOM. vibescript runs one render loop with one state object. freerange proves layout facts from source, no browser. Together: UI code that is deterministic, inspectable, and statically defensible — the floor AI-generated interfaces have been missing.
Turn any paragraph into pure numbers — height, line count, widest line — without a single DOM read.
Uses the browser's font engine as ground truth once, then all further answers are arithmetic over cached widths. No getBoundingClientRect, no reflow, no surprises at paint time.
One render loop. One state object. Reads batched, writes batched, events stored as transient state.
Game‑engine discipline for UI: inputs → layout → animation → commit → DOM writes. Animations have closed‑form solutions where possible. Depth has a single Z.ts. First paint uses the same data flow as every other frame.
Layout facts stated as @fit comments and proven from source. No browser, no fixtures, no sampled cases.
Preserved length, non‑negative sizes, monotone row tops, bounded column counts. The boring facts that catch real agent mistakes — stated once above the helper, earned once from the source, consumed at every call site.
rows.length == items.length always hold
can the column count ever reach 8
is width ever negative here
will this caption overflow at 320px
Reads and writes are segregated. Every pure helper in the middle column has a contract the source earned — so whether the frame is frame 0 or frame 10,000, the shape of the answer is the same thing the contract promised. No DOM reads during layout. No DOM writes before commit. Events are raw state, interpreted centrally.
A caption sized by its own content. Pretext measures it. Freerange proves the result fits. Vibescript consumes the numbers during the layout phase of its render loop. No single layer is doing the other's job.
// layout/caption.ts import { prepareWithSegments, layoutWithLines } from '@chenglou/pretext' /** @fit * given maxWidth: 120..800 * given lineHeight: 16..40 * result.height >= 0 * result.height <= maxWidth * 10 * result.widestLine <= maxWidth * result.lines[].width <= maxWidth * result.lines.length == result.lineCount */ export function measureCaption(text: string, font: string, maxWidth: number, lineHeight: number) { const prepared = prepareWithSegments(text, font) const { lines } = layoutWithLines(prepared, maxWidth, lineHeight) let widest = 0 for (const line of lines) if (line.width > widest) widest = line.width return { height: lines.length * lineHeight, lineCount: lines.length, widestLine: widest, lines, } }
caption.ts · 22 lines · one pure helper, one contract, three layers
The contract is small. The source earns it. The call sites stop guessing.
Now anywhere that calls measureCaption — the grid, the focused view, the overlay, a print layout — knows by construction that the caption fits its width and that the returned lines array length matches lineCount. No caller has to rediscover that fact with a console log.
If pretext says the caption fits in one line at width W, the painted DOM renders at width W too. "Measure against A, render at B" is a bug smell — assume so until proven otherwise.
Stable x/y/width/height is the source of truth. Animation is a visual residual on top. Hit testing, occlusion, scroll anchoring all read the base box, never the animated one.
Transforms, cursor, visibility, scrollTo, focus — all in the commit phase, at the end of render. Touching body.style.cursor during layout is still a DOM write.
Callbacks store the raw event and schedule a render. Nothing else. The render frame composes click + key + pointer together, so conflicting gestures resolve in one place.
Layout math belongs in @fit contracts on small helpers. Browser runs are for browser‑owned behavior: native selection, scroll physics, caret placement. Don't conflate them.
Not "the happy path works". Name the bug: clicking a caption selects the full prompt and stays in grid mode. Snapshot the thing a user would notice — ordered ids, visible row range, line count.
Small fixtures + one broader pass compressed away + one sensitivity check. "Tests pass" is too weak a stopping condition; "the check notices a meaningful regression" is the bar.